
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       gp_serial.c
  * @author     baiyang
  * @date       2021-7-15
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>

#include "gp_serial_device.h"
#include "gp_serial.h"
#include <common/console/console.h>
#include <parameter/param.h>
#include <board_config/borad_config.h>
/*-----------------------------------macro------------------------------------*/

/*----------------------------------typedef-----------------------------------*/
struct gp_serial_device serials[GP_SERIAL_COUNT_MAX];
/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/
static param_t var_info[] = {
    PARAM_DEFINE_INT8(SERIAL1_PROTOCOL,2),
    PARAM_DEFINE_INT32(SERIAL1_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL2_PROTOCOL,-1),
    PARAM_DEFINE_INT32(SERIAL2_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL3_PROTOCOL,5),
    PARAM_DEFINE_INT32(SERIAL3_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL4_PROTOCOL,-1),
    PARAM_DEFINE_INT32(SERIAL4_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL5_PROTOCOL,-1),
    PARAM_DEFINE_INT32(SERIAL5_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL6_PROTOCOL,-1),
    PARAM_DEFINE_INT32(SERIAL6_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL7_PROTOCOL,-1),
    PARAM_DEFINE_INT32(SERIAL7_BAUD,57),
    PARAM_DEFINE_INT8(SERIAL8_PROTOCOL,-1),
    PARAM_DEFINE_INT32(SERIAL8_BAUD,57),
};
PARAM_GROUP_DEFINE(SERIAL, var_info);

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
/*
 *  map from a 16 bit EEPROM baud rate to a real baud rate.  For
 *  stm32-based boards we can do 1.5MBit, although 921600 is more
 *  reliable.
 */
static uint32_t gp_serial_map_baudrate(int32_t rate)
{
    if (rate <= 0) {
        rate = 57;
    }
    switch (rate) {
    case 1:    return 1200;
    case 2:    return 2400;
    case 4:    return 4800;
    case 9:    return 9600;
    case 19:   return 19200;
    case 38:   return 38400;
    case 57:   return 57600;
    case 100:  return 100000;
    case 111:  return 111100;
    case 115:  return 115200;
    case 230:  return 230400;
    case 256:  return 256000;
    case 460:  return 460800;
    case 500:  return 500000;
    case 921:  return 921600;
    case 1500:  return 1500000;
    }

    if (rate > 2000) {
        // assume it is a direct baudrate. This allows for users to
        // set an exact baudrate as long as it is over 2000 baud
        return (uint32_t)rate;
    }

    // otherwise allow any other kbaud rate
    return rate*1000;
}

// protocol_match - returns true if the protocols match
static bool protocol_match(enum SerialProtocol protocol1, enum SerialProtocol protocol2)
{
    // check for obvious match
    if (protocol1 == protocol2) {
        return true;
    }

    // mavlink match
    if (((protocol1 == SerialProtocol_MAVLink) || (protocol1 == SerialProtocol_MAVLink2)) &&
        ((protocol2 == SerialProtocol_MAVLink) || (protocol2 == SerialProtocol_MAVLink2))) {
        return true;
    }

    // gps match
    if (((protocol1 == SerialProtocol_GPS) || (protocol1 == SerialProtocol_GPS2)) &&
        ((protocol2 == SerialProtocol_GPS) || (protocol2 == SerialProtocol_GPS2))) {
        return true;
    }

    return false;
}

/**
  * @brief       seirla分配对应的功能号
  * @param[in]   
  * @param[out]  
  * @retval      
  * @note        
  */
static void gp_serial_assign(void)
{
    serials[1].protocol = PARAM_GET_INT8(SERIAL, SERIAL1_PROTOCOL);
    serials[1].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL1_BAUD));

    serials[2].protocol = PARAM_GET_INT8(SERIAL, SERIAL2_PROTOCOL);
    serials[2].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL2_BAUD));

    serials[3].protocol = PARAM_GET_INT8(SERIAL, SERIAL3_PROTOCOL); 
    serials[3].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL3_BAUD));

    serials[4].protocol = PARAM_GET_INT8(SERIAL, SERIAL4_PROTOCOL);
    serials[4].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL4_BAUD));

    serials[5].protocol = PARAM_GET_INT8(SERIAL, SERIAL5_PROTOCOL);
    serials[5].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL5_BAUD));

    serials[6].protocol = PARAM_GET_INT8(SERIAL, SERIAL6_PROTOCOL);
    serials[6].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL6_BAUD));

    serials[7].protocol = PARAM_GET_INT8(SERIAL, SERIAL7_PROTOCOL);
    serials[7].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL7_BAUD));

    serials[8].protocol = PARAM_GET_INT8(SERIAL, SERIAL8_PROTOCOL);
    serials[8].baud = gp_serial_map_baudrate(PARAM_GET_INT32(SERIAL, SERIAL8_BAUD));
}
/**
  * @brief       将serial设备和uart设备一一对应，并将serial设备赋予功能
  * @param[in]   device:serial设备
  * @param[out]  
  * @retval      
  * @note        
  */
void gp_serial_device_init (void)
{
    gp_serial_assign();

#ifdef RT_USB_DEVICE_CDC
    serials[0].pname = "serial0";
    serials[0].puart = rt_device_find("vcom");  //serial[0]对应USB虚拟串口
    serials[0].protocol = SerialProtocol_MAVLink;
    serials[0].baud = 115200;
#ifdef RT_VCOM_TX_USE_DMA
    serials[0].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX | RT_DEVICE_FLAG_DMA_RX;
#else
    serials[0].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL1_DEVICE_NAME
    serials[1].pname = "serial1";
    serials[1].puart = rt_device_find(GP_SERIAL1_DEVICE_NAME);  //serial[1]对应serial1,所对应的uart由menuconfig用户配置
#ifdef GP_SERIAL1_TX_USING_DMA
    serials[1].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[1].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL1_RX_USING_DMA
    serials[1].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[1].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL2_DEVICE_NAME
    serials[2].pname = "serial2";
    serials[2].puart = rt_device_find(GP_SERIAL2_DEVICE_NAME);
#ifdef GP_SERIAL2_TX_USING_DMA
    serials[2].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[2].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL2_RX_USING_DMA
    serials[2].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[2].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL3_DEVICE_NAME
    serials[3].pname = "serial3";
    serials[3].puart = rt_device_find(GP_SERIAL3_DEVICE_NAME);
#ifdef GP_SERIAL3_TX_USING_DMA
    serials[3].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[3].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL3_RX_USING_DMA
    serials[3].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[3].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL4_DEVICE_NAME
    serials[4].pname = "serial4";
    serials[4].puart = rt_device_find(GP_SERIAL4_DEVICE_NAME);
#ifdef GP_SERIAL4_TX_USING_DMA
    serials[4].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[4].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL4_RX_USING_DMA
    serials[4].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[4].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL5_DEVICE_NAME
    serials[5].pname = "serial5";
    serials[5].puart = rt_device_find(GP_SERIAL5_DEVICE_NAME);
#ifdef GP_SERIAL5_TX_USING_DMA
    serials[5].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[5].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL5_RX_USING_DMA
    serials[5].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[5].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL6_DEVICE_NAME
    serials[6].pname = "serial6";
    serials[6].puart = rt_device_find(GP_SERIAL6_DEVICE_NAME);
#ifdef GP_SERIAL6_TX_USING_DMA
    serials[6].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[6].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL6_RX_USING_DMA
    serials[6].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[6].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL7_DEVICE_NAME
    serials[7].pname = "serial7";
    serials[7].puart = rt_device_find(GP_SERIAL7_DEVICE_NAME);
#ifdef GP_SERIAL7_TX_USING_DMA
    serials[7].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[7].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL7_RX_USING_DMA
    serials[7].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[7].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

#ifdef GP_SERIAL8_DEVICE_NAME
    serials[8].pname = "serial8";
    serials[8].puart = rt_device_find(GP_SERIAL8_DEVICE_NAME);
#ifdef GP_SERIAL8_TX_USING_DMA
    serials[8].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
#else
    serials[8].oflag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX;
#endif
#ifdef GP_SERIAL8_RX_USING_DMA
    serials[8].oflag |= RT_DEVICE_FLAG_DMA_RX;
#else
    serials[8].oflag |= RT_DEVICE_FLAG_INT_RX;
#endif
#endif

    char mutex_name[8];
    for(uint8_t i = 0; i < GP_SERIAL_COUNT_MAX; i++)
    {
        gp_serial_device_t serial = &serials[i];

        if(serial->puart != NULL)
        {
            if (i == 0) {
                serial->is_usb = true;
            } else {
                serial->is_usb = false;
            }

            switch(serial->protocol)
            {
                case SerialProtocol_None:
                    serial->rb_tx = rt_ringbuffer_create(64);
                    serial->rb_rx = rt_ringbuffer_create(64);
                    break;
                case SerialProtocol_Console:
                    serial->rb_tx = rt_ringbuffer_create(256);
                    serial->rb_rx = rt_ringbuffer_create(256);
                    break;
                case SerialProtocol_GPS:
                    serial->rb_tx = rt_ringbuffer_create(GP_SERIAL_GPS_BUFSIZE_TX);
                    serial->rb_rx = rt_ringbuffer_create(GP_SERIAL_GPS_BUFSIZE_RX);
                    break;

                case SerialProtocol_MAVLink:
                case SerialProtocol_MAVLink2:
                    serial->rb_tx = rt_ringbuffer_create(GP_SERIAL_MAV_BUFSIZE_TX);
                    serial->rb_rx = rt_ringbuffer_create(GP_SERIAL_MAV_BUFSIZE_RX);
                    break;

                case SerialProtocol_DDS:
                    serial->rb_tx = rt_ringbuffer_create(GP_SERIAL_DDS_BUFSIZE_TX);
                    serial->rb_rx = rt_ringbuffer_create(GP_SERIAL_DDS_BUFSIZE_RX);
                    break;

                default:
                    serial->rb_tx = rt_ringbuffer_create(GP_SERIAL_DEFAULT_BUFSIZE_TX);
                    serial->rb_rx = rt_ringbuffer_create(GP_SERIAL_DEFAULT_BUFSIZE_RX);
                    break;
            }

            if (!serial->rb_tx || !serial->rb_rx) {
                brd_config_error("function:%s, line number:%d, serials(%u) ringbuffer malloc failure\n",__FUNCTION__, __LINE__, i);
                continue;
            }

            gp_serial_device_register(serial, serial->pname, serial->oflag);

            // 在这里打开串口，线程中打开串口，因为线程优先级的问题，有可能其他高优先级的
            // 线程会使用没有打开的串口，造成死循环
            if (serial->protocol != SerialProtocol_None) {
                rt_device_open((rt_device_t)serial, serial->oflag); //oflag无用，通过宏配置
            }
        }
    }
}

/**
  * @brief       返回对应功能号串口的波特率
  * @param[in]   protocol  
  * @param[out]  
  * @retval      
  * @note        
  */
uint32_t SerialManager_find_baudrate(enum SerialProtocol protocol, uint8_t instance)
{
    gp_serial_device_t seri = (gp_serial_device_t)SerialManager_find_protocol_instance(protocol, instance);

    if (seri == NULL) {
        return 0;
    }

    return seri->baud;
}

/**
  * @brief       设置串口波特率
  * @param[in]   pSerial  
  * @param[in]   baudrate  
  * @param[out]  
  * @retval      
  * @note        
  */
int32_t SerialManager_set_baudrate(rt_device_t pSerial, uint32_t baudrate)
{
    gp_serial_device_t pSeri = (gp_serial_device_t)pSerial;

    if (pSerial == NULL) {
        return -1;
    }

    int32_t err = 0;
    struct serial_configure conf = RT_SERIAL_CONFIG_DEFAULT;
    conf.baud_rate = baudrate;

    rt_ringbuffer_reset(pSeri->rb_rx);

    err = rt_device_control(pSerial, RT_DEVICE_CTRL_CONFIG, &conf);

    return err;
}

/**
  * @brief       计算接受缓冲区接收到数据的大小
  * @param[in]   pSerial  
  * @param[out]  
  * @retval      
  * @note        
  */
uint16_t SerialManager_rx_available(rt_device_t pSerial)
{
    if (pSerial == NULL) {
        return 0;
    }

    gp_serial_device_t pSeri = (gp_serial_device_t)pSerial;

    /* disable interrupt */
    rt_base_t level = rt_hw_interrupt_disable();

    int16_t data_len = rt_ringbuffer_data_len(pSeri->rb_rx);

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    return data_len;
}

/**
  * @brief       计算发送缓冲区剩余大小
  * @param[in]   pSerial  
  * @param[out]  
  * @retval      
  * @note        
  */
uint16_t SerialManager_tx_space(rt_device_t pSerial)
{
    if (pSerial == NULL) {
        return 0;
    }

    gp_serial_device_t pSeri = (gp_serial_device_t)pSerial;

    /* disable interrupt */
    rt_base_t level = rt_hw_interrupt_disable();

    int16_t tx_space = rt_ringbuffer_space_len(pSeri->rb_tx);

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    return tx_space;
}

/**
  * @brief       
  * @param[in]   pSerial  
  * @param[in]   buffer  
  * @param[in]   size  
  * @param[out]  
  * @retval      
  * @note        
  */
uint32_t SerialManager_write(rt_device_t pSerial, const uint8_t *buffer, uint32_t size)
{
    if (pSerial == NULL) {
        return 0;
    }

    return rt_device_write(pSerial, 0, buffer, size);
}

uint32_t SerialManager_write_str(rt_device_t pSerial, const char *str)
{
    if (pSerial == NULL) {
        return 0;
    }

    return rt_device_write(pSerial, 0, (const uint8_t *)str, strlen(str));
}

uint32_t SerialManager_write_byte(rt_device_t pSerial, uint8_t byte)
{
    if (pSerial == NULL) {
        return 0;
    }

    return rt_device_write(pSerial, 0, (const uint8_t *)&byte, 1);
}

uint32_t SerialManager_read(rt_device_t pSerial, uint8_t *buffer, uint32_t size)
{
    if (pSerial == NULL) {
        return 0;
    }

    return rt_device_read(pSerial, 0, buffer, size);
}

int16_t SerialManager_read_byte(rt_device_t pSerial)
{
    uint8_t byte;

    if (pSerial == NULL) {
        return -1;
    }

    if (rt_device_read(pSerial, 0, &byte, 1) == 1) {
        return byte;
    } else {
        return -1;
    }
}

/*
  return timestamp estimate in microseconds for when the start of
  a nbytes packet arrived on the uart. This should be treated as a
  time constraint, not an exact time. It is guaranteed that the
  packet did not start being received after this time, but it
  could have been in a system buffer before the returned time.

  This takes account of the baudrate of the link. For transports
  that have no baudrate (such as USB) the time estimate may be
  less accurate.

  A return value of zero means the HAL does not support this API
*/
uint64_t SerialManager_receive_time_constraint_us(rt_device_t pSerial, uint16_t nbytes)
{
    if (pSerial == NULL) {
        return 0;
    }

    gp_serial_device_t pSeri = (gp_serial_device_t)pSerial;

    uint64_t last_receive_us = pSeri->receive_timestamp[pSeri->receive_timestamp_idx];
    if (pSeri->baud > 0 && !pSeri->is_usb) {
        // assume 10 bits per byte. For USB we assume zero transport delay
        uint32_t transport_time_us = (1000000UL * 10UL / pSeri->baud) * (nbytes + SerialManager_rx_available(pSerial));
        last_receive_us -= transport_time_us;
    }
    return last_receive_us;
}

rt_device_t SerialManager_find_protocol_instance(enum SerialProtocol protocol, uint8_t instance)
{
    uint8_t found_instance = 0;

    // search for matching protocol
    for(uint8_t i=0; i<GP_SERIAL_COUNT_MAX; i++) {
        if (protocol_match(protocol, (enum SerialProtocol)serials[i].protocol)) {
            if (found_instance == instance) {
                return &serials[i].parent;
            }
            found_instance++;
        }
    }

    // if we got this far we did not find the uart
    return NULL;
}

// find_portnum - find port number (SERIALn index) for a protocol and instance, -1 for not found
int8_t SerialManager_find_portnum(enum SerialProtocol protocol, uint8_t instance)
{
    const gp_serial_device_t _state = (gp_serial_device_t)SerialManager_find_protocol_instance(protocol, instance);
    if (_state == NULL) {
        return -1;
    }
    return (int8_t)(_state - &serials[0]);
}

enum SerialProtocol SerialManager_get_protocol(rt_device_t pSerial)
{
    gp_serial_device_t pSeri = (gp_serial_device_t)pSerial;

    return pSeri->protocol;
}

bool SerialManager_usb_connected()
{
    rt_bool_t connected = 0;

#ifdef RT_USB_DEVICE_CDC
    if (serials[0].is_usb && serials[0].puart != NULL) {
        connected = rt_device_control(serials[0].puart, RT_USBD_CLASS_CTRL_CONNECTED, &connected);
    }
#endif

    return connected;
}

uint32_t SerialManager_bw_in_kilobytes_per_second(rt_device_t pSerial)
{
    gp_serial_device_t pSeri = (gp_serial_device_t)pSerial;

    if (pSeri->is_usb) {
        return 200;
    }
    return pSeri->baud/(9*1024);
}

// get_serial_by_id - gets serial by serial id
rt_device_t SerialManager_get_serial_by_id(uint8_t id)
{
    if (id < GP_SERIAL_COUNT_MAX) {
        return &serials[id].parent;
    }

    return NULL;
}

/*------------------------------------test------------------------------------*/


